#pragma once

#include <iostream>
#include <vector>
#include <thread>
#include "loop_queue.hpp"
#include "task.hpp"

const size_t th_num = 5;

template<class T>
class thread_pool
{
public:
    static thread_pool* get_instance(size_t size = th_num)
    {
        if (_inst == nullptr)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            if (_inst == nullptr)
            {
                _inst = new thread_pool(size);
            }
        }
        return _inst;
    }

private:
    thread_pool(size_t size) : _cap(size)
    {
        for (int i = 0; i < _cap; i++)
        {
            _ths.emplace_back(routine, this);
        }
    }

    ~thread_pool()
    {}

    static void* routine(void* args)
    {
        thread_pool<T>* ts = static_cast<thread_pool<T>*>(args);

        while (true)
        {
            T t;
            ts->pop(&t);
            t();
        }
        return nullptr;
    }

public:
    void push(const T& t) { _tasks.push(t); }

    void pop(T* t) { _tasks.pop(t); }

private:
    std::vector<std::thread> _ths;
    size_t _cap;
    loop_queue<T> _tasks;

    static thread_pool* _inst;
    static std::mutex _mtx;
};

template<class T>
thread_pool<T>* thread_pool<T>::_inst = nullptr;
template<class T>
std::mutex thread_pool<T>::_mtx;

